﻿Imports System.Reflection, EnvDTE, EnvDTE100, EnvDTE80

''' <summary>
''' 
''' </summary>
''' <remarks></remarks>
''' <features></features>
''' <stepthrough></stepthrough>
Public NotInheritable Class Mod_Utilities
  
#Region "       Properties     >>>"

    Public Const ProjectWebsite As String = "http://extendedimmediatewin.codeplex.com/"
    Public Shared Property IsDebuggerConnected As Boolean
    <Obsolete("Why do I care if its Connected...If I can successfully Load the Main Form then I have Connected...", True)>
    Public Shared Property IsConnected As Boolean
    Public Shared Property LastStackFunction As String
    Public Shared Property ResultControl As ResultControl
    Public Shared Property SourceCode As String = "string msg = " & Environment.NewLine & vbTab & "string.Format(" & Environment.NewLine & vbTab & """Hello World from inside {0}!"", " & Environment.NewLine & vbTab & "this);" & Environment.NewLine & "System.Console.WriteLine(msg);" & Environment.NewLine & Environment.NewLine & "/* You can see the result of this intruding method if you turn on the visual studio output window. (menue: View -> Output) */"
    Public Shared Property Package As Microsoft.VisualStudio.Shell.Package
    Public Shared Property Compiler As Compiler
    Public Shared Property ToolWindowControl_Only As ToolWindowControl


    'Mod_Utilities.DTE.Debugger.CurrentStackFrame.Locals(1).DataMembers(2).Value

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    ''' <ComfortWrapper>True</ComfortWrapper>
    Public Shared ReadOnly Property Locals As List(Of LocalMember2)
        Get

            'Dim Test = Mod_Utilities.DTE.Debugger.CurrentStackFrame.Locals.GetRecursive(Nothing,
            '                                                                            Function(x, y)
            '                                                                                Dim This As New List(Of Object)
            '                                                                                For Each Item In x
            '                                                                                    y.Add(Item)
            '                                                                                Next
            '                                                                                Return y
            '                                                                            End Function)


            Dim List As New List(Of LocalMember2)
            For Each Item As EnvDTE100.Expression2 In Mod_Utilities.DTE.Debugger.CurrentStackFrame.Locals
                'Mod_Utilities.DTE.Debugger.CurrentStackFrame.Locals.Item(0).


                List.Add(New LocalMember2(Item))
            Next
            Return (List)
        End Get
    End Property

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="Name"></param>
    ''' <value></value>
    ''' <remarks></remarks>
    Public Shared ReadOnly Property Local(ByVal Name As String) As LocalMember2
        Get
            Return Locals.Find(Function(x) x.Name = Name)
        End Get
    End Property




    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    Public Shared ReadOnly Property CurrentFunctionName As String
        Get
            Return Mod_Utilities.DTE.Debugger.CurrentStackFrame.FunctionName
        End Get
    End Property







    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    ''' <ComfortWrapper>True</ComfortWrapper>
    Public ReadOnly Property CurrentFrame As EnvDTE.StackFrame
        Get
            Return Mod_Utilities.DTE.Debugger.CurrentStackFrame
        End Get
    End Property


    Private Shared _DTE As EnvDTE80.DTE2
    ''' <summary>
    ''' The Main Object Visual Studio Extensions. 
    ''' Warning If you Expand this when Debugging A VB.Net Project it will FUCK UP!
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    Public Shared ReadOnly Property DTE As EnvDTE80.DTE2
        Get
            'If Package Is Nothing Then Throw New ApplicationException("Package Is Nothing!!!")

            If _DTE Is Nothing Then 'This If Then Should Make things Much Faster
                Dim UnTyped As Object = Package
                _DTE = DirectCast(UnTyped.GetDTE2, EnvDTE80.DTE2)
            End If
            Return _DTE
        End Get
    End Property

    Public Shared ReadOnly Property LocalReachableMembers() As IList(Of LocalMember)
        Get
            Dim MyType = GetCurrentType() 'Use this...It should be Faster then ReCalling GetCurrentType Several Times

            Dim localFields = From e In DTE.Debugger.CurrentStackFrame.Locals.Cast(Of Expression2)()
                              Where Not e.Name.StartsWith("___")
                              Select New LocalMember(e.Name, e.Type, MemberType.Field)

            Dim publicInstanceProperties = From e In MyType.GetProperties()
                                           Select New LocalMember(e.Name, e.PropertyType.FullName, MemberType.Property, e.PropertyType) With
                                                  {.HasGetAccessor = e.CanRead, .HasSetAccessor = e.CanWrite}

            Dim publicStaticProperties = From e In MyType.GetProperties(BindingFlags.Static)
                                         Select New LocalMember(e.Name, e.PropertyType.FullName, MemberType.Property, e.PropertyType) With
                                                {.HasGetAccessor = e.CanRead, .HasSetAccessor = e.CanWrite}


            Dim nonPublicInstanceProperties = From e In MyType.GetProperties(BindingFlags.Instance Or BindingFlags.NonPublic)
                                              Select New LocalMember(e.Name, e.PropertyType.FullName, MemberType.Property, e.PropertyType) With
                                                     {.HasGetAccessor = e.CanRead, .HasSetAccessor = e.CanWrite}


            Dim nonPublicStaticProperties = From e In MyType.GetProperties(BindingFlags.Static Or BindingFlags.NonPublic)
                                            Select New LocalMember(e.Name, e.PropertyType.FullName, MemberType.Property, e.PropertyType) With
                                                   {.HasGetAccessor = e.CanRead, .HasSetAccessor = e.CanWrite}

            Dim reachableMembers = localFields.Union(publicInstanceProperties).Union(publicStaticProperties).Union(nonPublicInstanceProperties).Union(nonPublicStaticProperties).OrderBy(Function(a) a.Name)
            Dim result As List(Of LocalMember) = Nothing
            For i As Integer = 0 To 9
                Try
                    result = reachableMembers.ToList()
                Catch
                    System.Threading.Thread.Sleep(50)
                End Try
            Next
            Return result
        End Get
    End Property


#End Region


    ''' <summary>
    ''' 
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ''' <stepthrough></stepthrough>
    Public Shared Function GetContextType() As Type
        Dim resolver = GetResolver()

        'Why do I have the Line of Code Below?
        resolver.ReferencedAssemblies.ToList() '<-- Do I need this?

        Dim compiler As New Compiler("", resolver.ReferencedAssemblies, LocalReachableMembers.ToList(), LocalsMode.PrivateClassFields)
        compiler.Compile()
        Return compiler.InjectorType
    End Function


    ''' <summary>
    ''' System.Diagnostics.Debug.WriteLine(txt)
    ''' </summary>
    ''' <param name="txt"></param>
    ''' <remarks></remarks>
    ''' <stepthrough></stepthrough>
    <DebuggerNonUserCode()>
    Public Shared Sub Log(ByVal txt As String)
        System.Diagnostics.Debug.WriteLine(txt)
    End Sub




    '<Obsolete("Remove ASAP", True)>
    'Public Shared Function Connect() As Boolean
    '    If IsConnected = True OrElse ToolWindowControl_Only Is Nothing Then Return True
    '    Mod_Utilities.IsConnected = True
    '    Return True
    'End Function





    ' ''' <summary>
    ' ''' 
    ' ''' </summary>
    ' ''' <param name="sender"></param>
    ' ''' <param name="args"></param>
    ' ''' <returns></returns>
    ' ''' <remarks></remarks>
    ' ''' <stepthrough></stepthrough>
    '<Obsolete("Test", True)>
    'Public Shared Function CurrentDomain_AssemblyResolve(ByVal sender As Object, ByVal args As ResolveEventArgs) As Assembly
    '    If args.Name = GetCurrentType.Assembly.FullName Then
    '        Return GetCurrentType.Assembly
    '    Else
    '        Return Nothing
    '    End If
    'End Function


    '<Obsolete("Test A", True)>
    'Private Shared Sub UpdateContextTypeIfChanged()
    '    If IsConnected AndAlso DTE.Debugger.CurrentMode = dbgDebugMode.dbgBreakMode Then
    '        Dim currentFunction As String = DTE.Debugger.CurrentStackFrame.FunctionName

    '        Log("ExtendedImmediateAddInn - Connect")
    '        Log("Current Func: " & currentFunction)

    '        If currentFunction <> LastStackFunction Then
    '            Log("-> not cached already, have to analyze " & currentFunction)
    '            Log("CurrentType: " & GetCurrentType.ToString())

    '            Dim context = GetContextType()
    '            Log("ContextType (compiled in background): " & context.ToString())
    '            AddHandler AppDomain.CurrentDomain.AssemblyResolve, New ResolveEventHandler(AddressOf CurrentDomain_AssemblyResolve)

    '            Mod_Utilities.ToolWindowControl_Only.SetContextType(context)

    '            RemoveHandler AppDomain.CurrentDomain.AssemblyResolve, New ResolveEventHandler(AddressOf CurrentDomain_AssemblyResolve)
    '            LastStackFunction = currentFunction
    '            Log("Done.")
    '        End If
    '    End If
    'End Sub


End Class


Partial Class Mod_Utilities
    Private Sub New()
    End Sub

    ''' <summary>
    ''' Contains All Stuff Relating to dealing with Different Programming Languages and Frameworks
    ''' </summary>
    ''' <remarks></remarks>
    ''' <features></features>
    ''' <stepthrough></stepthrough>
    Public NotInheritable Class Versioning
        Private Sub New()
        End Sub


        ''' <summary>Gets the get current language. Make this Work with something other then Expressions</summary>
        Public Shared Function GetCurrentLanguage(ByVal expression As Expression2) As String
            Return expression.DataMembers.Parent.CurrentStackFrame.Language
        End Function




        Private Shared _Language As Languages = Languages.None
        ''' <summary>
        ''' 
        ''' </summary>
        ''' <value></value>
        ''' <remarks></remarks>
        Public Shared ReadOnly Property Language As Languages
            Get
                If _Language = Languages.None Then
                    If Mod_Utilities.DTE.Debugger.CurrentStackFrame.Language = "C#" Then
                        _Language = Languages.CSharp
                    Else
                        _Language = Languages.VB
                    End If
                End If
                Return _Language
            End Get
        End Property


        ''' <summary>Text</summary> 
        Public Enum Languages
            None
            VB
            CSharp
        End Enum

        ''' <summary>
        ''' 
        ''' </summary>
        ''' <value></value>
        ''' <remarks></remarks>
        Public Shared Property FrameWork As FrameWorks = FrameWorks.Four_Zero
        ''' <summary>Text</summary> 
        Public Enum FrameWorks
            Four_Zero
            Three_Five
        End Enum


        ''' <summary>
        ''' 
        ''' </summary>
        ''' <remarks></remarks>
        Public Shared ReadOnly Property CodeProviderSetting As Dictionary(Of String, String)
            Get
                Select Case FrameWork
                    Case FrameWorks.Three_Five
                        Return New Dictionary(Of String, String)() From {{"CompilerVersion", "v3.5"}}
                    Case Else
                        Return New Dictionary(Of String, String)() From {{"CompilerVersion", "v4.0"}}
                End Select
            End Get
        End Property


        ''' <summary>
        ''' Formats the Code so it has A ; at the End if it <see cref="Language"/> = C#
        ''' </summary>
        ''' <param name="Code">The Code you want to Format</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        ''' <stepthrough>Enabled</stepthrough>
        <DebuggerStepThrough()>
        Public Shared Function FormatEnd(ByVal Code As String) As String
            Return If(Language = Languages.CSharp, Code & ";", Code)
        End Function



    End Class


End Class